% !TeX spellcheck = en_US
\lstdefinelanguage[Objective]{C}[GNU99]{C}
  {morekeywords={@catch,@class,@encode,@end,@finally,@implementation,%
      @interface,@private,@protected,@protocol,@public,@selector,%
      @synchronized,@throw,@try,BOOL,Class,IMP,NO,Nil,SEL,YES,_cmd,%
      bycopy,byref,id,in,inout,nil,oneway,out,self,super,%
      % The next two lines are Objective-C 2 keywords.
      @dynamic,@package,@property,@synthesize,readwrite,readonly,%
      assign,retain,copy,nonatomic%
      },%
   moredirectives={import}%
  }%

\lstdefinelanguage[GNU99]{C}[99]{C}
  {morekeywords={asm,__asm__,__extension__,typeof,__typeof__}%
  }%

\lstdefinelanguage[99]{C}%
  {morekeywords={_Bool,_Complex,_Imaginary,auto,break,case,char,%
      const,continue,default,do,double,else,enum,extern,float,for,%
      goto,if,inline,int,long,register,restrict,return,short,signed,%
      sizeof,static,struct,switch,typedef,union,unsigned,void,volatile,%
      while},%
   sensitive,%
   morecomment=[s]{/*}{*/},%
   morecomment=[l]//,%
   morestring=[b]",%
   morestring=[b]',%
   moredelim=*[directive]\#,%
   moredirectives={define,elif,else,endif,error,if,ifdef,ifndef,line,%
      include,pragma,undef,warning}%
  }[keywords,comments,strings,directives]%
  
\chapter{图像的获取与渲染}

图像的传输和显示是\dicom 标准中主要描述的部分，并且在其中就使用HTTP协议如何进行图像的传输进行了专门的说明，形成了一个专门的WADO协议子集。所以在\dicom 转 HTTP 代理中，参照WADO协议，实现了图像获取和显示功能部分。

\section{WADO 协议介绍}

WADO协议定义在\dicom 标准 Part 18: Web Access to DICOM Persistent Objects(WADO)\cite{plain:dcmPart}中。此标准规范了通过Web服务访问和显示\dicom 图像的实现方式。
主要用于向临床应用发布检查报告和图像。它提供了一种简单的机制，使得客户端应用程序可以使用各种\dicom 唯一标识符，通过HTTP/HTTPs协议来取得\dicom 图像数据。
客户端既可以要求获得普通格式的图像（如JPEG或GIF格式），也可以要求获得原始的\dicom 格式图像。WADO标准侧重于图像的获得和展示，不提供查找图像的功能。\footnote{翻译自\dicom Part 18: Web Access to DICOM Persistent Objects Scope部分}

从上述的协议描述中可以看出，不同于非图像信息查询，在\dicom 标准中描述了使用HTTP协议获取图像的技术规范。目前已经有不少PACS系统中提供了WADO服务功能，
但是在很多老旧的PACS并没有此功能，所以仍然有必要在\dicom \ To Http 代理中添加实现WADO协议相关功能。

WADO协议使用HTTP GET方法发送请求，必须带有一个名为\texttt{requestType},值为WADO的参数，可选的参数有：studyUID（表示登记UID），seriesUID（表示序列UID），
objectUID（表示图像UID）和contentType（表示返回的图像格式，默认为JPEG图像格式）等。

在WADO标准文档中，示范了一个简单的获取图像请求可以为：

\fbox{\shortstack[l]{http://www.hospital-stmarco/radiology/wado.php?requestType=WADO\ \ \ \ \ \ \ \ \ \ \  \\\&studyUID=1.2.250.1.59.40211.12345678.678910\\\&seriesUID=1.2.250.1.59.40211.789001276.14556172.67789\\\&objectUID=1.2.250.1.59.40211.2678810.87991027.899772.2}} 

一个获取\dicom 格式的请求可以为：

\fbox{\shortstack[l]{http://www.medical-webservice.st/RetrieveDocument?requestType=WADO\ \ \ \ \ \  \\\&studyUID=1.2.250.1.59.40211.12345678.678910\\\&seriesUID=1.2.250.1.59.40211.789001276.14556172.67789\\\&objectUID=1.2.250.1.59.40211.2678810.87991027.899772.2 \\\&contentType=application\%2Fdicom}} 


\section{WADO 协议的实现}

\subsection{CMove、CGet协议}

要实现WADO协议，首先需要能从\dicom 服务器上获取图像。在传统的\dicom 协议中，SCU端有CMove和CGet两种获取图像的方法。CMove方法兼容性好，被广泛支持，但是需要事先配置好目的地AE的网络参数，而且很难穿越防火墙。CGet方法只在较新的PACS系统中被支持，但是可以穿越防火墙且无需事先配置网络参数。\cite{plain:d14}

\begin{figure}  [H]
%\bifigcaption{fig:cmove}{CMove 方法}{CMove Service}
	\centering
	\begin{tikzpicture}[node distance = 2cm, auto]
	% Place nodes
	
	\node [block] (movescu){MoveSCU};
	
	\node [block,right=4cm of movescu.east] (movescp){MoveSCP};
	
	\node [block,below=3cm  of movescu.south ,xshift=2cm] (movedest){MoveDest StoreSCP};
	
	%edges
	
	\draw[line]([yshift=0.25cm]movescu.east) -- node{\zihao{5}\circletext{1}发送Move 请求} ([yshift=0.25cm]movescp.west);
	\draw[line]([xshift=-0.25cm]movescp.south) -- node[above,sloped]{\zihao{5}\circletext{2}发送Store请求和数据} ([xshift=-0.25cm]movedest.north);
	\draw[line]([xshift=0.25cm]movedest.north) -- node[below,sloped]{\zihao{5}\circletext{3}发送Store应答} ([xshift=0.25cm]movescp.south);
	\draw[line]([yshift=-0.25cm]movescp.west) -- node{\zihao{5}\circletext{4}发送Move应答} ([yshift=-0.25cm]movescu.east);
	
	\end{tikzpicture}
\bifigcaption{fig:cmove}{CMove 方法}{CMove Service}
\end{figure}

\begin{figure} [H]
%\bifigcaption{fig:cget}{CGet 方法}{CGet Service}
	\centering
	\begin{tikzpicture}[node distance = 2cm, auto]
	% Place nodes
	\draw (0,0) node[minimum height=3cm,draw](getscu) {
		\begin{tabular}{c}
		GetSCU \\
		StoreSCP
		\end{tabular}
	}
	(5,0) node[minimum height=3cm,draw](getscp) {GetSCP};
	
	\draw[line]([yshift=1cm]getscu.east) -- node[above]{\zihao{5}\circletext{1}发送Get请求}([yshift=1cm]getscp.west);
	\draw[line]([yshift=.3cm]getscp.west) -- node[above]{\zihao{5}\circletext{2}发送Store请求}([yshift=.3cm]getscu.east);
	\draw[line]([yshift=-0.3cm]getscu.east) -- node[above]{\zihao{5}\circletext{3}发送Store应答}([yshift=-0.3cm]getscp.west);
	\draw[line]([yshift=-1cm]getscp.west) -- node[above]{\zihao{5}\circletext{4}发送Get应答}([yshift=-1cm]getscu.east);
	
	\end{tikzpicture}
\bifigcaption{fig:cget}{CGet 方法}{CGet Service}
\end{figure}


无论CMove还是CGet方法，都是先由SCU向SCP端发出一个包含StudyUID，SeriesUID和InstanceUID的数据元素集合作为请求（如果是CMove方法，还需要设定目的地AETitle）。SCP接收到请求后找到相应的图像，如果是CMove方法，则与目的地AE建立一个新的\dicom 连接，然后通过CStore方法向目的地AE发送图像；如果是CGet方法，则利用现有的与SCU的\dicom 连接，同样通过CStore方法向SCU端发送图像。可见使用CMove需要SCP端事先知道目的地AE的网络参数，并且能直接访问到目的地AE，
如果目的地AE在一个使用NAT方案进行连接的防火墙之后的话（移动设备往往使用此方案），将不能成功发送图像。而CGet方法的所有数据通讯均在一个\dicom 连接内完成，只需SCU能连接到SCP端即可（类似于HTTP协议），也无需事先维护目的地AE，但是只能将图像发送到请求端，没有CMove那样灵活。
CMove方法的一个常用场景是由报告工作站发起，向存储服务器请求图像，并将图像发送到阅片图像工作站上进行显示，由于过去硬件技术的限制（多屏显示技术不成熟），早期的PACS多使用这种模式。而使用CGet方法的话则要求报告工作站和阅片工作站是同一个SCU，适用于现代的支持多显示器的图像显示。\cite{plain:d17}





另外，CMove和CGet SCU 都必须提供CStore服务，用以接收图像。CStore是在\dicom 标准中定义的用来规范图像存放的操作。一般是由检查设备生成\dicom 格式的文件后，向PACS系统请求存储文件。在CMove和CGet方法调用中，灵活应用了CStore进行图像传输，统一了图像发送和图像接收的处理机制。

在\dcmfourche 类库中，提供了基本的StoreSCP服务框架，只要实现了自定义的存储\javamethod{store}方法，就能实现一个简单的存储SCP端。

\begin{lstlisting}[language=JAVA,caption=定义StoreSCP]
private BasicCStoreSCP storageSCP = new BasicCStoreSCP("*") {
    @Override
    protected void store(Association as, PresentationContext pc, Attributes rq,
                         PDVInputStream data, Attributes rsp) throws IOException {
        if (dicomStreamStore == null)
            return;
        String iuid = rq.getString(Tag.AffectedSOPInstanceUID);
        String cuid = rq.getString(Tag.AffectedSOPClassUID);
        String tsuid = pc.getTransferSyntax();
        try { dicomStreamStore.store(as,
                    as.createFileMetaInformation(iuid, cuid, tsuid), data);
        } catch (Exception e) {
            throw new DicomServiceException(Status.ProcessingFailure, e);
        }}};
\end{lstlisting}

\subsection{使用\dcmfourche 进行图像格式转换}

在WADO协议中，规定了可以通过指定Content Type 为 JPEG 来获得可以直接在浏览器端渲染的通用图像格式文件。所以在能够取得原始\dicom 格式图像的基础上，需要编程实现将\dicom 格式图像转换为一般图像的功能。在\dcmfourche 中实现了一系列JAVA ImageIO图像格式转换接口，可以方便的将\dicom 格式图像转换为任意格式图像。

\begin{lstlisting}[language=JAVA,caption=使用\dcmfourche 读取\dicom 文件为Java 图像]
static public BufferedImage dcm2pic(DicomInputStream inputStream) throws IOException {
    ImageInputStream iis = ImageIO.createImageInputStream(inputStream);
    DicomImageReaderSpi spi = new DicomImageReaderSpi();
    ImageReader reader = spi.createReaderInstance();
    reader.setInput(iis);
    BufferedImage image = reader.read(0);
    return image;
}
\end{lstlisting}

\subsection{WADO Web服务的实现}
参照\dicom 标准中所规定的WADO URL规范，使用Spring Web MVC 框架开发了\javaclass{WADOContrller}类，提供了图像访问服务。由于是要将图像发送到请求端，所以使用了CGet方法来获取图像。
\begin{lstlisting}[language=JAVA,caption=WADO Controller]
@RequestMapping("wado")
@Controller
public class WADOController {
    @Autowired
    CGetService getService;
    static public final String dicomMimetype = "application/dicom";
    @RequestMapping(value="",method = RequestMethod.GET, params="requestType=WADO")
    public void wado(
                       @RequestParam(value="studyUID") String studyUID,
                       @RequestParam(value="seriesUID") String seriesUID,
                       @RequestParam(value="objectUID") String objectUID,
                       @RequestParam(value="contentType", 
                       required = false,
                       defaultValue = "image/jpeg") String contentType,
                       HttpServletResponse response)
            throws DicomException, IOException {
        response.setContentType(contentType);
        DicomInputStream inputStream = getService.cget(studyUID,seriesUID,objectUID);
        if(inputStream == null)
            return;
        if (contentType.equals(dicomMimetype)){
            FileCopyUtils.copy(inputStream,response.getOutputStream());
        }else{
            BufferedImage image = ImageHelper.dcm2pic(inputStream);
            ImageHelper.saveImage(image,response.getOutputStream(),contentType);
        } }}
\end{lstlisting}

为了方便起见，将CGet方法调用中的细节部分抽象到了类\javaclass{CGetService}中，而\javaclass{CGetService}最主要的功能是使用\javaclass{GetSCU}类来取得\dicom 图像，
在\javaclass{GetSCU}类中，由\javamethod{get}方法实现此功能。\javamethod{get}方法类似\javaclass{FindSCU}中的\javamethod{find}方法，同样接受一个\javaclass{Attributes}类型参数作为请求，不过返回类型为\javaclass{DicomInputStream},表示有SCP端发送来的图像数据流。根据\dicom 标准中规范的CGet方法调用流程可知，当SCP确认图像发送完成后，才会发送Get完成应答，所以需要在\javamethod{get}方法中对SCP的应答包进行判断，如果最后的应答包状态不等于\javaobject{Status.Success}，说明并没有成功的接受图像，需要抛出\javaclass{GetException}供调用者进一步处理。
\begin{lstlisting}[language=JAVA,caption=\javaclass{GetSCU}中的\javamethod{get}方法]
public DicomInputStream get(Attributes attributes) throws GetException {
    if (dicomStreamStore == null)
        return null;
    try {
        String sopUID = attributes.getString(Tag.SOPInstanceUID);
        DimseRSP rsp = getAsync(attributes);
        int lastStatus = -1;
        while (rsp.next()) {
            Attributes command = rsp.getCommand();
            lastStatus = command.getInt(Tag.Status, -1);
            if (Status.isPending(lastStatus))
                continue;
        }
        if (lastStatus != Status.Success) {
            throw new GetException("status:" + lastStatus + " is not Success");
        }
        return dicomStreamStore.fetch(sopUID);
    } catch (IOException | InterruptedException e) {
        throw new GetException(e.getMessage());
    }}
\end{lstlisting}

\section{\dicom 图像在移动客户端的显示}

在服务器端实现了WADO协议后，使得客户端的开发得到了简化，尤其是图像获得环节，直接使用由CFind服务取得的相关参数构造出URL访问即可。如果客户端要求不高的话，直接在客户端嵌入浏览器控件即可实现\dicom 图像显示功能，如果客户端有较专业的功能要求，可能需要在移动端进行\dicom 文件的解析、处理和显示操作。




\subsection{直接使用浏览器进行渲染}

\begin{figure}[H]
%\bifigcaption{fig:wadoInSafari}{Safari 中的显示效果}{Display image in Safari}
\centering
\scalebox{0.2}{\includegraphics{WADODisplay.png}}
\bifigcaption{fig:wadoInSafari}{Safari 中的显示效果}{Display image in Safari}
\end{figure}

由于WADO协议默认返回JPEG格式的图像文件，并且在所有平台的浏览器中都能很好的支持JPEG图像，所以可以直接使用浏览器控件用来显示\dicom 文件。

在开发此类移动客户端时，只需要获得必要的请求参数，构造出符合WADO协议的URL，直接使用类似\javaclass{UIWebView}这样的浏览器控件，就可以在客户端显示\dicom 图像。这样做的好处是方便，开发快，缺点是仅能显示图像而已，没有缩放、缩略图、调窗等任何其他功能。\cite{plain:d22}

\subsection{使用WADO取得图像后显示}

如果是使用WADO协议来简化图像获取和解析的话，可以在客户端简单进行编程，通过StudyUID、SeriesUID和InstanceUID三个参数构成URL，使用客户端HTTP相关接口来获得图像文件，然后使用各个平台的图像显示API如\javaclass{UIImage}等进行渲染。\cite{plain:d24}
\lstset{language=[Objective]C, breakindent=40pt, breaklines}
\begin{lstlisting}[caption=在iOS下通过WADO协议获得图像]
const NSString* DICOMURL =@"http://javaee.oritsh.com/wado-service/wado?requestType=WADO&studyUID=%@&seriesUID=%@&objectUID=%@&annotation=patient,technique";

for(int i=0;i<_dataArray.count;i++){
    NSDictionary* data = [_dataArray objectAtIndex:i];
    NSString* seriesInstanceUID = [data valueForKey:@"SeriesInstanceUID"];
    NSString* studyInstanceUID  = [data valueForKey:@"StudyInstanceUID"];
    NSString* sopInstanceUID    = [data valueForKey:@"SOPInstanceUID"];
    //`构造URL`
    NSURL* url = [NSURL URLWithString:
                  [ NSString stringWithFormat:(NSString*)DICOMURL,
                   studyInstanceUID,seriesInstanceUID,sopInstanceUID] ];
    ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
    NSDictionary* userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
                              [NSNumber numberWithInt:i],@"index",
                              @"move",@"type",
                              nil];
    
    request.userInfo = userInfo;
    
    [request setDelegate:self];
    
    request.timeOutSeconds = 60;
    
    [self.requestQueue addOperation:request];
}

...

//`当接受到图像时会自动调用此方法`
- (void)requestFinished:(ASIHTTPRequest *)request
{
   ...
   
    // `当以二进制形式读取返回内容`
    NSData *responseData = [request responseData];
   
   ... 
   
    UIImageView * imageView =
        [[UIImageView alloc]initWithFrame:CGRectMake(0,0, smallFrameHeight*0.9, smallFrameHeight*0.9)];
    imageView.image = [UIImage imageWithData:responseData];
    UIView* preview = [self.viewArray objectAtIndex:index];
    [preview addSubview:imageView];
        
   ...   
}
\end{lstlisting}

\begin{figure}
%\bifigcaption{fig:wadoInIOS}{iOS系统下应用WADO协议进行图像显示}{Using WADO to display image in iOS}
\centering
\scalebox{0.35}{\includegraphics{wadoIOS.png}}
\bifigcaption{fig:wadoInIOS}{iOS系统下应用WADO协议进行图像显示}{Using WADO to display image in iOS}
%\label{fig:wadoInIOS}
\end{figure}

这样做的有点是开发比较方便，并且有简单的缩放、缩略图功能，缺点是不支持调窗等专业功能。

\subsection{在客户端解析 \dicom  文件进行渲染}

使用WADO协议直接获取JPEG图像的技术方案使得客户端的编程开发得到了很大的简化，但是这会使得图像格式转换相关的计算量都由服务器端承担。如果移动客户端数量较多，并且终端处理器性能较高的话，可以考虑使用WADO协议来获取原始的\dicom 文件，由移动终端来解析和渲染图像，虽然这种方式需要较多的开发工作量，但是可以支持全部的\dicom 图像浏览查看功能。


一个\dicom 格式文件所储存的就是一个\dicom 数据元素集合，解析\dicom 文件就是指读取该该数据元素集合。\dcmfourche 框架提供了相关方法可以方便读取\dicom 文件中的数据元素。但是各种移动终端系统平台的差异较大，只有Android系统能够支持\dcmfourche 运行，
而iOS和其他系统都不能支持Java，也就不能利用\dcmfourche 进行开发。而JavaScript语言在主流的移动终端平台上广泛支持，所以以JavaScript编写的解析\dicom 文件的程序对移动设备有很好的兼容性。下面的代码演示了如何将\dicom 文件解析为JavaScript字典对象。

\begin{lstlisting}[language=JAVA,caption=使用JavaScript解析\dicom 数据元素集合]
var DicomReader = $.buildClass({
    ...
//`读取数据元素标签`
find_element_Implicit:function(){
	var self= this;
	//`先读取8个字节用于确定数据元素标签`
	self.stream.read(8,function(data,size){
		if(size != 8){
			//`读取的数据长度不为8则说明文件终止`
			self.on_stream_end();
		}
		else{
			//`读取数据标签并在标准字典中查找此标签的元数据`
			var raw = jspack.Unpack('<2HI',data);
			var key = "0x%04x 0x%04x".sprintf(raw[0],raw[1]);
			var meta = __dicom_dict__[key];
			if(meta != undefined && meta != null){
					var head=
					{
						key:key,//`元素标签`
						type:meta.type,//`数据类型`
						length:raw[2],//`数据长度`
						title:meta.title,//`元素标题`
						size:meta.size//`数据个数 length=sizeof(type)*size`
					}
			}else{
				if(raw[0]==0x0009){
					var head=
					{
						key:key,
						type:'CS',
						length:raw[2],
						title:key,
						size:-1
					}

				}else{
					console.log("undefined element:0x%04x 0x%04x".sprintf(raw[0],raw[1]))
				}
			}
			self.on_found_element(head);
		}
	});
},

...
//`读取数据内容`
find_data:function(head){
	var self = this;
	var read_data = function(length){
		self.stream.read(length,function(data,size){
			if(length != size){
				self.on_stream_end();	
			}else{
				var dtype = head.type;
				var jtype = __dicom2js__[dtype];
				if(jtype != undefined && jtype != null){
					var parse = '<%d%s'.sprintf(head.size,jtype);
					var j_v   = jspack.Unpack(parse,data);
					self.on_found_data(head,j_v);
				}else{

					self.on_found_data(head,data);
				}
			}
		});
	}
	var length = head.length;
	if(head.type == 'OB'){//`对OB和OW类型数据的特殊处理`
		self.stream.read(4,function(data,size){
			var length = jspack.Unpack('<I',data)[0];
			read_data(length);	
		});
	} else if(head.type == 'OW'){
		self.stream.read(4,function(data,size){
			var length = jspack.Unpack('<I',data)[0];
			read_data(length);
		});
	}else{
		read_data(length);
	}
},

	...

on_found_element:function(head){
	this.find_data(head);
},
on_found_data:function(head,value){
    //将数据元素标签和数据元素内容插入到数据元素集合中
	this.image[head.title]=value;
}
});
\end{lstlisting}

解析出数据元素字典后，就可以根据\dicom 标准中所定义的规则，渲染出图像。要能显示一副\dicom 图像，必须的数据元素在表\ref{tab:EssTag}中列出。

\begin{table}
	\bitabcaption{tab:EssTag}{显示\dicom 图像必须的数据元素}{Essential Data Element Tag of displaying image}
\centering
\begin{tabular}{lp{6cm}p{6cm}}
\hline
标签 & \dcmfourche 中的常量名称 &	说明 \\
\hline
0x00280010 & Tag.Rows & 图像高度 \\
\hline
0x00280011 & Tag.Columns & 图像宽度 \\
\hline
0x00281050 & Tag.WindowCenter & 窗位 \\
\hline
0x00281051 & Tag.WindowWidth & 窗宽 \\
\hline
0x00281052 & Tag.RescaleIntercept & 校正截距 \\
\hline
0x00281053 & Tag.RescaleSlope & 校正斜率 \\
\hline
0x00280100 & Tag.BitsAllocated & 为每个像素分配的位数 \\
\hline
0x00280101 & Tag.BitsStored & 每个像素实际使用的位数 \\
\hline
0x7FE00010 & Tag.PixelData & 实际图像数据 \\
\hline
\end{tabular}%

%\label{tab:EssTag}
\end{table}

对于未压缩的\dicom 文件,取得表\ref{tab:EssTag}中的数据元素后，可以知道图像数据长度 $  = Rows \times Columns \times BitsAllocated \div 8 $。因为BitsStored 表示实际使用了多少个比特位用于存放数据，
所以原始数据值可以表示为:$OriginalValue = StoreValue \ \& \ (2^{BitsStored}-1) $。在得到原始值后，通过下面的公式计算CT值:

\begin{equation}
CTValue = OriginalValue \times RescaleSlope + RescaleIntercept
\end{equation}

然后，根据窗宽窗位得到最小和最大可见CT值:

\begin{equation}
\begin{split}
Lowest &= WindowCenter - WindowWidth / 2 \\
Highest &= WindowCenter + WindowWidth / 2  
\end{split}
\end{equation}

最后，得到每一像素的灰度值为:

    \begin{equation}
PixelValue = \left\{
\begin{array}{rl}
    &0 \qquad CTValue \le Lowest \\
    &255 \qquad CTValue \ge Highest \\
    &\rule{0ex}{2em}\dfrac{255\times (CTValue-Lowest)}{WindowWidth} \qquad Lowest \le CTValue \le Highest
\end{array}
\right.
    \end{equation} 
    
得到每一个像素的灰度值后，就可以使用各平台API进行绘图渲染了。


\section{图像信息的标注（四角文字渲染）}

在WADO标准中，包含了对图像附加文字标注的规范。可以在请求URL中包含\texttt{annotation}参数用以指定需要添加那些标注。例如:\texttt{annotation=patient,technique}
表示添加病人基本信息和检查信息。在JAVA中，可以使用\javaclass{Graphic2D}对象进行绘图操作，此对象的\javamethod{drawString}方法可以放置文字标签，比如可以使用下面的代码进行添加病人基本信息：
\begin{lstlisting}[language=JAVA,caption=添加病人基本信息标注]
static public BufferedImage imageAddPatientAnnotation(BufferedImage image,Attributes datasets){
    //BufferedImage img = imageCopy(image);
    Graphics2D g2d = image.createGraphics();
    g2d.setColor(Color.RED);
    g2d.setFont(new Font("SansSerif",Font.BOLD,18));
    //`读取病人编号、姓名、出生日期`
    List<String> patientInfo = Arrays.asList(
            datasets.getString(Tag.PatientID,null),
            datasets.getString(Tag.PatientName,null),
            datasets.getString(Tag.PatientBirthDate,null)
    );
    int y=20;int x = 0;
    for (String info : patientInfo){
        if (info == null)
            continue;
        else{
            x = image.getWidth() - 12*info.length();
            g2d.drawString(info,x,y);
            y+=22;
        }
    }
    return image;
}
\end{lstlisting}

对于检查信息，组织机构信息也可以使用类似的方法添加标注。

%\subsection{图像缩放、旋转、位移、窗宽窗位调整等处理}

%\subsection{长度、角度、面积的测量}

\section{小结}

图像传输功能是本项目的重要功能。
本章根据\dicom 标准中的WADO协议规范，实现了\dicom \ to \ HTTP Proxy 中的图像传输服务部分。
在本章中首先介绍了WADO协议，说明了如何构造图像请求URL等协议细节。并结合\dcmfourche 第三方类库说明了如何实现WADO服务，本章的WADO实现中包含获取不同格式图像和图像文字标注功能。
然后讨论了一种不同的客户端图像获取和现实方式。